// uvm_tb/agent/binary_alu_driver.sv
`ifndef BINARY_ALU_DRIVER_SV
`define BINARY_ALU_DRIVER_SV

class binary_alu_driver extends uvm_driver #(binary_alu_transaction);

    // ========================================================================
    // Data Members
    // ========================================================================
    protected virtual binary_alu_if vif;

    // ========================================================================
    // UVM Macros
    // ========================================================================
    `uvm_component_utils(binary_alu_driver)

    // ========================================================================
    // Constructor
    // ========================================================================
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    // ========================================================================
    // UVM Build Phase
    // ========================================================================
    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        if (!uvm_config_db#(virtual binary_alu_if)::get(this, "", "vif", vif))
            `uvm_fatal("NOVIF", "Virtual interface must be set for binary_alu_driver")
    endfunction

    // ========================================================================
    // UVM Run Phase
    // ========================================================================
    virtual task run_phase(uvm_phase phase);
        // Initialize all input signals to idle state
        init_signals();

        // Wait for reset to complete
        @(posedge vif.rst_n);
        `uvm_info("DRIVER", "Reset released, driver ready", UVM_LOW)

        forever begin
            seq_item_port.get_next_item(req);
            `uvm_info("DRIVER", $sformatf("Driving transaction for mode %0d", req.mode), UVM_MEDIUM)

            // Drive configuration
            drive_config(req);

            // Drive data inputs in parallel
            fork
                if (req.send_top) drive_data_port("top", req.top_data, req.top_sdb);
                if (req.send_left) drive_data_port("left", req.left_data, req.left_sdb);
                if (req.send_bottom) drive_data_port("bottom", req.bottom_data, req.bottom_sdb);
            join

            `uvm_info("DRIVER", "Transaction complete", UVM_MEDIUM)
            seq_item_port.item_done();
        end
    endtask

    // ========================================================================
    // Signal Initialization
    // ========================================================================
    protected virtual task init_signals();
        // Configuration interface
        vif.cfg_vld    = 1'b0;
        vif.cfg_mode   = 3'b0;
        vif.cfg_scalar = '0;

        // Top input interface
        vif.top_vld    = 1'b0;
        vif.top_dat    = '0;
        vif.top_sdb    = '0;

        // Left input interface
        vif.left_vld   = 1'b0;
        vif.left_dat   = '0;
        vif.left_sdb   = '0;

        // Bottom input interface
        vif.bottom_vld = 1'b0;
        vif.bottom_dat = '0;
        vif.bottom_sdb = '0;

        `uvm_info("DRIVER", "All input signals initialized to idle state", UVM_MEDIUM)
    endtask

    // ========================================================================
    // Helper Tasks
    // ========================================================================

    // Drive configuration interface
    protected virtual task drive_config(binary_alu_transaction trans);
        int timeout_count = 0;

        @(posedge vif.clk);
        vif.cfg_vld    <= 1'b1;
        vif.cfg_mode   <= trans.mode;
        vif.cfg_scalar <= trans.scalar;

        `uvm_info("DRIVER", "Waiting for cfg_rdy handshake...", UVM_HIGH)

        // Wait for handshake with timeout
        fork
            begin
                wait (vif.cfg_vld && vif.cfg_rdy);
                `uvm_info("DRIVER", "cfg_rdy received", UVM_HIGH)
            end
            begin
                repeat (1000) @(posedge vif.clk);
                `uvm_error("DRIVER", "Timeout waiting for cfg_rdy!")
            end
        join_any
        disable fork;

        // @(posedge vif.clk);
        // vif.cfg_vld <= 1'b0;
    endtask

    // Drive a single data port
    protected virtual task drive_data_port(string port_name, aru_dat_t data, aru_sdb_t sdb);
        case (port_name)
            "top": begin
                @(posedge vif.clk);
                vif.top_vld <= 1'b1;
                vif.top_dat <= data;
                vif.top_sdb <= sdb;

                `uvm_info("DRIVER", $sformatf("Waiting for %s_rdy handshake...", port_name), UVM_HIGH)
                fork
                    begin
                        wait (vif.top_vld && vif.top_rdy);
                        `uvm_info("DRIVER", $sformatf("%s_rdy received", port_name), UVM_HIGH)
                    end
                    begin
                        repeat (1000) @(posedge vif.clk);
                        `uvm_error("DRIVER", $sformatf("Timeout waiting for %s_rdy!", port_name))
                    end
                join_any
                disable fork;

                @(posedge vif.clk);
                vif.top_vld <= 1'b0;
            end
            "left": begin
                @(posedge vif.clk);
                vif.left_vld <= 1'b1;
                vif.left_dat <= data;
                vif.left_sdb <= sdb;

                `uvm_info("DRIVER", $sformatf("Waiting for %s_rdy handshake...", port_name), UVM_HIGH)
                fork
                    begin
                        wait (vif.left_vld && vif.left_rdy);
                        `uvm_info("DRIVER", $sformatf("%s_rdy received", port_name), UVM_HIGH)
                    end
                    begin
                        repeat (1000) @(posedge vif.clk);
                        `uvm_error("DRIVER", $sformatf("Timeout waiting for %s_rdy!", port_name))
                    end
                join_any
                disable fork;

                @(posedge vif.clk);
                vif.left_vld <= 1'b0;
            end
            "bottom": begin
                @(posedge vif.clk);
                vif.bottom_vld <= 1'b1;
                vif.bottom_dat <= data;
                vif.bottom_sdb <= sdb;

                `uvm_info("DRIVER", $sformatf("Waiting for %s_rdy handshake...", port_name), UVM_HIGH)
                fork
                    begin
                        wait (vif.bottom_vld && vif.bottom_rdy);
                        `uvm_info("DRIVER", $sformatf("%s_rdy received", port_name), UVM_HIGH)
                    end
                    begin
                        repeat (1000) @(posedge vif.clk);
                        `uvm_error("DRIVER", $sformatf("Timeout waiting for %s_rdy!", port_name))
                    end
                join_any
                disable fork;

                @(posedge vif.clk);
                vif.bottom_vld <= 1'b0;
            end
        endcase
    endtask

endclass : binary_alu_driver

`endif  // BINARY_ALU_DRIVER_SV
